/**
 +-------------------------------------------------------------------
 * FontScroll - 文字行向上滚动插件
 * 说明： 
 * 		1、改成通过$("li:first").appendTo("ul")的方式实现向上滚动
 * 		2、添加参数：
 * 		 	timeoutMethod
 * 		 	scrollBehavior
 * 		 	ul
 * 		 	li
 * 作者：konger123 （随枫的落叶 konger@126.com）
 * gitee：https://gitee.com/konger123/FontScroll
 * 
 * 原项目github：https://gitee.com/kzm/FontScroll
 * 原作者：kongzhim
 +-------------------------------------------------------------------
 * @version    1.0.2
 * @since      2020.09.05
 * @author     konger123 <konger@126.com>
 * @gitee     https://gitee.com/konger123/FontScroll
 +-------------------------------------------------------------------
 */

var FontScroll = (function () {

    /**
     * 创建方法
     * @param target 滚动目标div的ID或选择器
     * @options 参数：
     * 		time：每次滚动的时间间隔(单位：毫秒)，默认3000
     * 		speed：滚动动画时间(单位：毫秒)，数值越小滚动动画速度越快，默认500
     * 		timeoutMethod: 延时时间方式：interval-使用setInverval处理；timeout-使用setTimeout处理
     * 		rowClass：滚动选中行的样式，默认：fontColor
     * 		num：滚动选中的行序号，默认：0 表示第一行
     * 		skip：滚动时上面留白显示的行数，默认0
     * 		scrollBehavior: 滚动动画与行样式的先后顺序：rowFirst先加行样式，再动画滚动；scrollFirst先动画滚动，再加行样式
     * 		pauseOnHover：鼠标经过时是否暂停，默认：true
     *		startOnInit：初始化后是否自动运行，默认：true
     * 		init：是否默认初始化，默认true
     * 		ul: 滚动的行的外部容器（选择器），用于获取滚动区域的实际高度
     * 		li: 滚动行（选择器）
     */
    var FontScroll = function (target, options) {
        var _this = this;
        this.version = '1.0.2';
        this.defaults = {
            time: 3000,	//每次滚动的时间间隔(单位：毫秒)，默认3000
            speed: 500,	//滚动动画时间(单位：毫秒)，数值越小滚动动画速度越快，默认500
            timeoutMethod: 'interval', //延时时间方式：interval-使用setInverval处理；timeout-使用setTimeout处理
            rowClass: 'fontColor', //滚动选中行的样式，默认：fontColor
            num: 0,  //滚动时选中的行序号，默认：0 表示第一行
            skip: 0,//滚动时上面留白显示的行数，默认0
            scrollBehavior: 'rowFirst', //滚动动画与行样式的先后顺序：rowFirst先加行样式，再动画滚动；scrollFirst先动画滚动，再加行样式
            pauseOnHover: true,	//鼠标经过时是否暂停，默认：true
            startOnInit: true,	//初始化后是否自动运行，默认：true
            init: true,	//是否默认初始化
            ul: 'ul', //滚动的行的外部容器（选择器），用于获取滚动区域的实际高度
            li: 'li', //滚动行（选择器）
        };
        this.options = $.extend({}, this.defaults, options);
        if (this.options.speed >= this.options.time) {//防止给定的动画时间不足
            this.options.speed = this.options.time - 10;
        }
        if (this.options.num > this.options.skip) {//防止越界
            this.options.num = this.options.skip;
        }

        this.target = target;
        this.el = (typeof target === 'string') ? $(target) : $(target);
        this.ul = null;//ul对象
        this.li = null;
        this.length = 0;//li的个数

        this.elH = 0;
        this.ulH = 0;

        this.timeID = null;//timer的ID
        this.cursor = 0;
        this.inited = false;

        if (this.options.init) {
            this.init();
        }

        if (this.options.startOnInit) {
            this.start();
        }
    }

    /**
     * 初始化
     */
    FontScroll.prototype.init = function () {
        if (this.el == undefined || this.el == null) {
            throw new Error("初始化失败，对象是否已被销毁？请检查！");
        }

        if (this.inited) {
            return this;
        }

        this.ul = this.el.find(this.options.ul).first();//ul对象
        if (this.ul == null || this.ul.length < 1) {
            throw new Error("初始化失败，未找到'" + this.options.ul + "'对象");
        }
        this.li = this.ul.find(this.options.li);
        this.length = this.li.length;

        this.elH = this.el.height();
        this.ulH = this.ul.height();

        this.timeID = null;//timer的ID
        this.cursor = this.options.num;

        if (this.length > 0) {
            this.options.num = this.options.num % this.length;
            this.options.skip = this.options.skip % this.length;
            if (this.options.num > this.options.skip) {
                this.options.num = this.options.skip;
            }

            this.cursor = this.options.num % this.length;

            this.li.removeClass(this.options.rowClass);
            this.li.eq(this.cursor).addClass(this.options.rowClass);
        }

        this.inited = true;
        return this;
    }

    /**
     * 更新参数
     * @param options 新的参数
     * @param immediatelyEffect 新参数是否立即生效
     */
    FontScroll.prototype.updateOptions = function (options, immediatelyEffect) {
        this.options = $.extend({}, this.options, options);

        if (immediatelyEffect) {
            if (this.timeID != null) {
                //如果正在运行的话，使新参数立即生效
                this.pause();
                this.resume();
            }
        }
        return this;
    }

    /**
     * 开始运行
     */
    FontScroll.prototype.start = function () {
        if (this.timeID != null) {
            return this;
        }
        if (this.length < 1) {
            return this;
        }
        if (!this.inited) {
            this.init();
        }

        var _this = this;

        if (_this.options.timeoutMethod == 'interval') {
            _this.timeID = setInterval(function () {
                _this.up();
            }, _this.options.time);
        } else {
            _this.timeID = setTimeout(function () {
                _this.up();
            }, _this.options.time);
        }

        if (_this.options.pauseOnHover) {
            _this.ul.hover(function () {
                if (_this.timeID != null) {
                    if (_this.options.timeoutMethod == 'interval') {
                        clearInterval(_this.timeID);
                    } else {
                        clearTimeout(_this.timeID);
                    }
                    _this.timeID = null;
                }
            }, function () {
                if (_this.options.timeoutMethod == 'interval') {
                    _this.timeID = setInterval(function () {
                        _this.up();
                    }, _this.options.time);
                } else {
                    _this.timeID = setTimeout(function () {
                        _this.up();
                    }, _this.options.time);
                }
            });
        }

        return this;
    }

    /**
     * 继续
     * @returns {Number} timeID
     */
    FontScroll.prototype.resume = function () {
        return this.start();
    }

    /**
     * 向上滚动一行
     * @param resetDelay 当延时方法为timeout时，是否在滚动后要重新计算延时
     */
    FontScroll.prototype.up = function (resetDelay) {
        if (!this.inited) {
            this.init();
        }

        var _this = this;

        if (_this.length > 0) {
            _this.cursor++;
            _this.cursor = _this.cursor % _this.length;

            var liH = _this.li.eq(0).outerHeight();

            //先加行样式，后滚动
            if (_this.options.scrollBehavior == "rowFirst") {
                _this.li.removeClass(_this.options.rowClass);
                _this.li.eq(_this.options.skip + 1).addClass(_this.options.rowClass);
            }

            var rowSelectedIndex = -1;
            _this.li.each(function (i, o) {
                if (rowSelectedIndex < 0 && $(o).hasClass(_this.options.rowClass)) {
                    rowSelectedIndex = i;
                    return;
                }
            });
            if (rowSelectedIndex > -1 && rowSelectedIndex < _this.options.skip) {//选中行还没到skip的位置
                _this.li.removeClass(_this.options.rowClass);
                _this.li.eq(rowSelectedIndex + 1).addClass(_this.options.rowClass);
            } else {
                //先加行样式，后滚动
                if (_this.options.scrollBehavior == "rowFirst") {
                    _this.li.removeClass(_this.options.rowClass);
                    _this.li.eq(_this.options.skip + 1).addClass(_this.options.rowClass);
                }

                //滚动动画
                _this.ul.animate({ marginTop: '-' + liH }, _this.options.speed, function () {
                    _this.ul.animate({ marginTop: 0 }, 10, function () {
                        _this.li.eq(0).appendTo(_this.li.eq(0).parent());
                        _this.li = _this.ul.find(_this.options.li);
                        //先滚动，后加行样式
                        if (_this.options.scrollBehavior != "rowFirst") {
                            _this.li.removeClass(_this.options.rowClass);
                            _this.li.eq(_this.options.skip).addClass(_this.options.rowClass);
                        }
                    });
                });
            }
        }

        if (_this.timeID != null) {
            if (_this.options.timeoutMethod == 'timeout') {
                clearTimeout(_this.timeID);
                _this.timeID = setTimeout(function () {
                    _this.up();
                }, _this.options.time);
            } else {
                if (resetDelay) {
                    clearInterval(_this.timeID);
                    _this.timeID = setInterval(function () {
                        _this.up();
                    }, _this.options.time);
                }
            }
        }
        return this;
    }

    /**
     * 暂停
     */
    FontScroll.prototype.pause = function () {
        if (!this.inited) {
            this.init();
        }

        if (this.options.pauseOnHover) {
            //取消hover事件
            this.ul.unbind('mouseenter').unbind('mouseleave');
        }
        if (this.timeID != null) {
            if (this.options.timeoutMethod == 'interval') {
                clearInterval(this.timeID);
            } else {
                clearTimeout(this.timeID);
            }
            this.timeID = null;
        }
        return this;
    }

    /**
     * 停止
     */
    FontScroll.prototype.stop = function () {
        if (!this.inited) {
            this.init();
        }

        var _this = this;

        //取消事件
        if (this.options.pauseOnHover) {
            //取消hover事件
            this.ul.unbind('mouseenter').unbind('mouseleave');
        }
        //停止滚动
        if (this.timeID != null) {
            if (this.options.timeoutMethod == 'interval') {
                clearInterval(this.timeID);
            } else {
                clearTimeout(this.timeID);
            }
            this.timeID = null;
        }

        //滚动还原
        this.cursor = this.options.num;

        if (this.length > 0) {
            this.cursor = this.options.num % this.length;

            this.li.removeClass(this.options.rowClass);
            this.li.eq(this.cursor).addClass(this.options.rowClass);
        }

        this.ul.animate({ marginTop: '0' }, 10);

        return this;
    }

    /**
     * 销毁对象。销毁后对象会变成"{}"
     */
    FontScroll.prototype.destroy = function () {
        this.stop();
        for (var a in this) {
            delete this[a];
        }
    }

    /**
     * 添加li
     * @param o 要添加的li对象。可以是"<li>...</li>"这样的字符串，也可以是dom对象
     */
    FontScroll.prototype.addLi = function (o) {
        if (!this.inited) {
            throw new Error("清先初始化后再进行此操作！");
        }

        var dom = $(o);
        if (this.length < 1) {
            this.ul.append(dom);
        } else {
            this.cursor = this.cursor % this.length;
            dom.insertAfter(this.li.eq(this.length - this.cursor + this.options.skip - 1));
        }
        this.li = this.ul.find(this.options.li);
        this.length = this.li.length;

        this.elH = this.el.height();
        this.ulH = this.ul.height();

        return this;
    }

    /**
     * 移除li
     * @param selector 要移除li的选择器。如"#li_1"、"li:first"、"li:last"、"li:eq(2)"等等
     */
    FontScroll.prototype.removeLi = function (selector) {
        if (!this.inited) {
            throw new Error("清先初始化后再进行此操作！");
        }

        this.ul.find(selector).remove();
        this.li = this.ul.find(this.options.li);
        this.length = this.li.length;

        this.elH = this.el.height();
        this.ulH = this.ul.height();

        if (this.cursor == this.length - 1) {
            this.cursor = 0;
        }

        return this;
    }

    /**
     * 加法函数，用来得到精确的加法结果
     * 说明：javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
     * 调用：accAdd(arg1,arg2)
     * 参数：arg1被加数，arg2加数
     * 返回值：arg1加上arg2的精确结果
     */
    var accAdd = function (arg1, arg2) {
        var r1, r2, m;
        try {
            r1 = arg1.toString().split(".")[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg2.toString().split(".")[1].length;
        } catch (e) {
            r2 = 0;
        }
        m = Math.pow(10, Math.max(r1, r2));
        return (arg1 * m + arg2 * m) / m;
    }

    return FontScroll;
}());